﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using eTerm.PlugIn.NCore;
using eTerm.MsSql.DbProvider;
using System.Text.RegularExpressions;
using eTerm.ASynClientSDK;
using System.Net.Mail;

namespace eTerm.PlugIn.AutoSeatDetail
{
    /// <summary>
    /// 按照航段明细试订座
    /// <remarks>
    ///     将PNR方式、明细方式分开预订，间化预订逻辑
    /// </remarks>
    /// </summary>
    public class DetailModePlugIn:BaseSocketPlugIn,IPlugIn
    {
        protected Queue<PnrRequire> __PnrQueue = new Queue<PnrRequire>();
        private PnrSegment __CurrentSegment = null;
        private List<BookAirSeg> __airSegList = new List<BookAirSeg>();
        private string OutPacketName = string.Empty;

        /// <summary>
        /// 连接认证成功，开始指令序列.
        /// </summary>
        protected override void StartQueue()
        {
            PrepareQueue();
            Dequeue();
        }


        /// <summary>
        /// 插件授权码.
        /// </summary>
        /// <value>The plug in code.</value>
        protected override int PlugInCode
        {
            get { return 128; }
        }

        /// <summary>
        /// 准备数据队列.
        /// </summary>
        protected virtual void PrepareQueue() {
            PnrRequire.All().Where(l => string.IsNullOrEmpty(l.PnrCode) && (l.StatusId & (int)(PNRRequireStatus.ERROR | PNRRequireStatus.APPROVED)) != 0).OrderBy(l => l.RecordDate).ToList().ForEach(l =>
            {
                __PnrQueue.Enqueue(l);
            });
        }

        private void Dequeue()
        {
            if (__PnrQueue.Count == 0)
            {
                FireOnExecuted();
                return;
            }
            __airSegList = new List<BookAirSeg>();              //清除上一需求行程列表
            PnrRequire model = __PnrQueue.Dequeue();
            AV(model);
        }


        private void AV(PnrRequire model)
        {
            PnrSegment segment = PnrSegment.All().Where(l => l.RequireId == model.Id /*&& !l.IsInvalid*/).OrderBy(l => l.Id).FirstOrDefault();
            __CurrentSegment = segment;
            if (__CurrentSegment == null)
            {
                Dequeue();
                return;
            }
            SendStream(BuildAVCommand(segment));
        }


        /// <summary>
        /// 根据需求航段生成查询指令.
        /// </summary>
        /// <param name="segment">The segment.</param>
        /// <returns>System.String.</returns>
        private string BuildAVCommand(PnrSegment segment) {
            StringBuilder sb = new StringBuilder(@"AVH/");
            Match m = Regex.Match(segment.FlightNo, @"([A-Z0-9]{2})\d+", RegexOptions.IgnoreCase | RegexOptions.Multiline);
            sb.AppendFormat(@"{0}{1}", segment.Departure, segment.Arrival);
            sb.AppendFormat(@"/{0}", getDateString(segment.DepartureDate.Value));
            if (!string.IsNullOrEmpty(segment.StartRangeTime))
                sb.AppendFormat(@"/{0}", segment.StartRangeTime);
            if (m.Success){
                sb.AppendFormat(@"/{0}", m.Groups[1].Value);
            }

            sb.Append(@"/D");
            return sb.ToString();
        }

        private void AV(PnrSegment model)
        {
            PnrSegment segment = PnrSegment.All().Where(l => l.RequireId == model.RequireId && l.Id > model.Id /*&& !l.IsInvalid*/).OrderBy(l =>l.Id ).FirstOrDefault();
            if (segment == null)
            {

                //待预订航段
                __airSegList.ForEach(l => {
                    FireOnNotify(l.getairNo, EventType.INFO, string.Format(@"待订航段：{0}-{1}<{2}> {3}", l.getorgCity, l.getdesCity, l.getfltClass, l.departureTime.ToString(@"yyyy-MM-dd")));
                });
                SentMail(model.RequireId, __airSegList);
                Dequeue();
                return;
            }
            __CurrentSegment = segment;
            SendStream(BuildAVCommand(segment));
        }


        /// <summary>
        /// 检测到可用航班立即通知提交人
        /// </summary>
        /// <param name="model"></param>
        /// <param name="airSegList"></param>
        protected virtual void SentMail(long? modelKey, List<BookAirSeg> airSegList) {

            if (airSegList.Count == 0) return;

            StringBuilder sb = new StringBuilder(@"<Result>");
            var model = PnrRequire.All().Where(l => l.Id == (modelKey ?? 0))
                    .Join(SysUser.All(), l => l.UserId, r => r.UserId, (l, r) => new {r.DisplayName,l.RequireCode,l.RecordDate,r.Email }).Distinct().FirstOrDefault();
                    


            sb.AppendFormat(@"<DisplayName>{0}</DisplayName>", model.DisplayName);
            sb.AppendFormat(@"<RequireCode>{0}</RequireCode>", model.RequireCode);
            sb.AppendFormat(@"<RecordDate>{0}</RecordDate>", model.RecordDate.ToString(@"yyyy-MM-dd HH:mm"));

            sb.Append(@"<BookAirSegs>");
            PnrSegment.All().Where(l => l.RequireId == modelKey).ToList().ForEach(l => {
                //sb.AppendFormat(ToXml<PnrSegment>(l, @"BookAirSeg"));
                sb.Append(@"<BookAirSeg>");

                sb.AppendFormat(@"<Id>{0}</Id>", l.Id);
                sb.AppendFormat(@"<Arrival>{0}</Arrival>", l.Arrival);
                sb.AppendFormat(@"<CabinCode>{0}</CabinCode>", l.CabinCode);
                sb.AppendFormat(@"<Departure>{0}</Departure>", l.Departure);
                sb.AppendFormat(@"<DepartureDate>{0}</DepartureDate>", l.DepartureDate.Value.ToString(@"yyyy-MM-dd"));
                sb.AppendFormat(@"<FlightNo>{0}</FlightNo>", l.FlightNo);
                sb.AppendFormat(@"<AvailableCabin>{0}</AvailableCabin>",string.Join(";", airSegList.Where(h=>h.TotalSeconds==l.Id.ToString()).Select(h=>string.Format(@"{0}:{1}",h.getairNo, h.getfltClass)).ToArray()));

                sb.Append(@"</BookAirSeg>");
            });
            sb.Append(@"</BookAirSegs>");

            sb.AppendFormat(@"<BookPassengers>");
            PnrPassenger.All().Where(l => l.RequireId == modelKey).ToList().ForEach(l => {
                //sb.AppendFormat(ToXml<PnrPassenger>(l, @"BookPassenger"));
                sb.Append(@"<BookPassenger>");
                sb.AppendFormat(@"<Id>{0}</Id>", l.Id);
                sb.AppendFormat(@"<IdentityNo>{0}</IdentityNo>", l.IdentityNo);
                sb.AppendFormat(@"<PassengerName>{0}</PassengerName>", l.PassengerName);

                sb.AppendFormat(@"<PNRPassengerType>{0}</PNRPassengerType>", GetDescription((PNRPassengerType)l.PassengerType));
                sb.AppendFormat(@"<PNRIdentityType>{0}</PNRIdentityType>", GetDescription((PNRPassengerIdentityType)l.IdentityType));
                sb.Append(@"</BookPassenger>");
            });

            sb.AppendFormat(@"</BookPassengers>");

            sb.Append(@"</Result>");
            SendEmail(sb.ToString(), new System.Net.Mail.MailAddressCollection() { new MailAddress(model.Email, model.DisplayName) }, new MailAddressCollection(), new MailAddressCollection());
        }

        /// <summary>
        /// 插件配置文件名.
        /// </summary>
        /// <value>插件配置文件名.</value>
        protected override string SetupXmlFile
        {
            get
            {
                return @"AutoSeatPlugIn.xml";
            }
        }


        private void Assign(PnrSegment model, List<Flight> segments, string PakcetName, string PakcetResult)
        {
            //自动订座
            Match m = Regex.Match(model.FlightNo, @"([A-Z0-9]{2})\d+", RegexOptions.IgnoreCase | RegexOptions.Multiline);
            if (m.Success)
                AssignByFlightNo(model, segments, OutPacketName, PakcetResult);               //指定航班模式
            else
                AssignByCabins(model, segments, OutPacketName, PakcetResult);


            //记录指令历史
            new PnrSentHistory()
            {
                GroupCode = GetSessionId,
                PacketName = PakcetName,
                PacketResult = PakcetResult,
                RequireId = model.RequireId,
                SentDate = DateTime.Now
            }.Save();

        }

        /// <summary>
        /// 指令发送后回调函数.
        /// </summary>
        /// <param name="PacketName">Name of the packet.</param>
        protected override void AfterSentPacket(string PacketName)
        {
            OutPacketName =Regex.IsMatch(PacketName,@"^PN", RegexOptions.IgnoreCase| RegexOptions.Multiline)?OutPacketName:PacketName;
        }


        /// <summary>
        /// 是否含有一下页数据.
        /// </summary>
        /// <param name="Cmd">The CMD.</param>
        /// <param name="Result">The result.</param>
        /// <returns><c>true</c> if [has AV next page] [the specified CMD]; otherwise, <c>false</c>.</returns>
        private bool HasAVNextPage(string Cmd, string Result)
        {
            string queryDate = Regex.Match(Cmd, @"AVH\/[A-Z]{6}\/(\w+)\/", RegexOptions.IgnoreCase | RegexOptions.Multiline).Groups[1].Value;
            queryDate = queryDate.Substring(0, 5);
            return Regex.IsMatch(Result, queryDate, RegexOptions.IgnoreCase | RegexOptions.Multiline);
        }

        /// <summary>
        /// 根据指令航班需求选择航班.
        /// </summary>
        /// <param name="model">The model.</param>
        /// <param name="segments">The segments.</param>
        /// <param name="cmd">The CMD.</param>
        /// <param name="result">The result.</param>
        protected virtual void AssignByFlightNo(PnrSegment model, List<Flight> segments, string cmd, string result)
        {
            FireOnNotify(string.Format(@"{0}-{1}<{2}--{3}>", model.Departure, model.Arrival, model.FlightNo, model.CabinCode), EventType.INFO, string.Format(@"开始使用指定航班模式匹配用户需求航班及舱位", @""));
            List<PnrPassenger> passengers = PnrPassenger.All().Where(l => l.RequireId == model.RequireId).ToList();
            Flight fmodel = segments.ToList().Where(t => (t.Carbins.Where(g => g.Number == "A" || int.Parse(g.Number) >= passengers.Count).Count() > 0) && string.Format(@"{0}{1}", t.Airline, t.FlightNO) == model.FlightNo).Select(l => new Flight { 
                FlightNO= l.FlightNO, 
                DepartureAirport= l.DepartureAirport,
              ArrivalAirport = l.ArrivalAirport,
               Airline = l.Airline,
                DepartureTime=l.DepartureTime,
                 FltDate = l.FltDate,
                Carbins = l.Carbins.ToList()
                .Join(Regex.Matches(model.CabinCode, @"([A-Z]\d?)", RegexOptions.IgnoreCase | RegexOptions.Multiline).Cast<Match>().ToList(),q=>q.Carbin,v=>v.Groups[1].Value,(q,v)=>q)
                .Where(g => g.Number == "A" || int.Parse(g.Number) >= passengers.Count).ToList()
            }).FirstOrDefault();
            if (fmodel != null&&fmodel.Carbins.Count>0) { //预订座位
                //SendSSCommand(fmodel, passengers);
                __airSegList.Add(new BookAirSeg() {
                 getairNo=string.Format(@"{0}{1}",fmodel.Airline,fmodel.FlightNO),
                  getorgCity = fmodel.DepartureAirport,
                   getdesCity = fmodel.ArrivalAirport,
                    getfltClass =string.Join(",", fmodel.Carbins.Select(c=>c.Carbin).ToArray()),
                     departureTime = model.DepartureDate.Value,
                 TotalSeconds = model.Id.ToString()
                });
                AV(model);
                return;
            }
            else if (fmodel == null && !HasAVNextPage(cmd, result))
            {
                model.IsInvalid = true;
                FireOnNotify(string.Format(@"{0}-{1}<{2}--{3}>", model.Departure, model.Arrival, model.FlightNo, model.CabinCode), EventType.ERROR, string.Format(@"{0}-{1}<{2}--{3}>航段无效", model.Departure, model.Arrival, model.FlightNo, model.CabinCode));
                AV(model);
            }
            else if (HasAVNextPage(cmd, result))
            {
                SendStream(@"PN");
                return;
            }
            else {
                AV(model);
            }

            model.Save();
        }


        /// <summary>
        /// 预订座位.
        /// </summary>
        /// <param name="fmodel">The fmodel.</param>
        /// <param name="passengers">The passengers.</param>
        private void SendSSCommand(Flight fmodel, List<PnrPassenger> passengers) { 
            
        }

        /// <summary>
        /// 根据舱位需求选择航班.
        /// </summary>
        /// <param name="model">The model.</param>
        /// <param name="segments">The segments.</param>
        /// <param name="cmd">The CMD.</param>
        /// <param name="result">The result.</param>
        protected virtual void AssignByCabins(PnrSegment model, List<Flight> segments, string cmd, string result)
        {
            List<PnrPassenger> passengers = PnrPassenger.All().Where(l => l.RequireId == model.RequireId).ToList();
            FireOnNotify(string.Format(@"{0}-{1}<{2}>", model.Departure, model.Arrival, model.CabinCode), EventType.INFO, string.Format(@"开始使用指定舱位模式用户需求航班及舱位", @""));
            
            var i = Regex.Matches(model.CabinCode, @"([A-Z0-9]{2})\:([A-Z]\d?[\,]?)+\;", RegexOptions.IgnoreCase | RegexOptions.Multiline).Cast<Match>().ToList()
                .Select(l => new PnrSegment() { FlightNo = l.Groups[1].Value, LSTFK_PNR_REQU_REFERENCE_PNR_SEGM = l.Groups[2].Captures.Cast<Capture>().Select(c => new PnrRequireCode() { CabinCode = Regex.Replace(c.Value, @"\,", string.Empty, RegexOptions.IgnoreCase) }).ToList() });

            var j = segments
                .Join(i, l => l.Airline, r => r.FlightNo, (l, r) => new
                {
                    FlightNo = string.Format(@"{0}{1}", l.Airline, l.FlightNO),
                    l.DepartureAirport,
                    l.ArrivalAirport,
                    l.Airline,
                    RequireCabins =r.LSTFK_PNR_REQU_REFERENCE_PNR_SEGM.Select(h=>h.CabinCode).ToList(),
                    Cabins = l.Carbins.Where(h => h.Number == "A" || int.Parse(h.Number) >= passengers.Count).Select(h=>h.Carbin).ToList()
                    //Cabins = l.Carbins.Where(h => h.Number == "A" || int.Parse(h.Number) >= passengers.Count).Join(r.LSTFK_PNR_REQU_REFERENCE_PNR_SEGM, t => t.Carbin , q => q.CabinCode , (t, q) => t.Carbin).ToList()
                })
                //.Join(r.LSTFK_PNR_REQU_REFERENCE_PNR_SEGM,t=>t.Carbin,q=>q.CabinCode,(t,q)=>t.Carbin  )
                .ToList();
            var toDoList = j
                .Select(l => new { l.FlightNo, l.Airline, l.DepartureAirport, l.ArrivalAirport, Carbins = l.Cabins.Join(l.RequireCabins, h => h, b => b, (h, b) => b).ToList() })
                .Where(u => u.Carbins.Count > 0)
                //.GroupBy(l => l.FlightNo, (Key, chars) => string.Format(@"{0}:{1}", Key, string.Join(",",  chars.Select(z=>z.Carbins.   )))
                .GroupBy(l => l.FlightNo, (FlightNo, CabinKeys) => new { FlightNo, CarbinStr = string.Join(",", CabinKeys.Select(q => string.Join(",", q.Carbins.ToArray())).ToArray()) })
                .Join(j, w => w.FlightNo, g => g.FlightNo, (w, g) => new { g.FlightNo, g.DepartureAirport, g.ArrivalAirport, g.Airline, w.CarbinStr }).ToList();
                
            if (toDoList.Count > 0)
            {
                toDoList.ForEach(fmodel =>
                {
                    __airSegList.Add(new BookAirSeg()
                    {
                        getairNo = fmodel.FlightNo,
                        getorgCity = fmodel.DepartureAirport,
                        getdesCity = fmodel.ArrivalAirport,
                        getfltClass = fmodel.CarbinStr,
                        departureTime = model.DepartureDate.Value,
                        TotalSeconds = model.Id.ToString()
                    });
                });
                AV(model);
                return;
            }
            else if (toDoList.Count == 0 && HasAVNextPage(cmd, result))
            {
                SendStream(@"PN");
                return;
            }
            else{
                model.IsInvalid = true;
                FireOnNotify(string.Format(@"{0}-{1}<{2}--{3}>", model.Departure, model.Arrival, model.FlightNo, model.CabinCode), EventType.ERROR, string.Format(@"{0}-{1}<{2}--{3}>航段无效", model.Departure, model.Arrival, model.FlightNo, model.CabinCode));
                AV(model);
            }

            model.Save();
        }


        /// <summary>
        /// 指令结果分析.
        /// </summary>
        /// <param name="PakcetName">指令名称.</param>
        /// <param name="PakcetResult">指令结果.</param>
        protected override void ExecutePacket(string PakcetName, string PakcetResult)
        {
            if (Regex.IsMatch(PakcetName, @"^(PN|AV)", RegexOptions.IgnoreCase | RegexOptions.Multiline))
            {     //航班查询指令结果处理
                List<Flight> items = new List<Flight>();
                //if(!HasAVNextPage(PakcetName,PakcetResult))
                items = new AnalysisAVH().ParseAVH(PakcetName, PakcetResult).Flights;
                if (items != null && items.Count > 0)
                {
                    Assign(__CurrentSegment, items, PakcetName, PakcetResult);
                }
            else {
                    AV(__CurrentSegment);
                }
            }
            
        }

        /// <summary>
        /// 完成插件后调用方法.
        /// </summary>
        protected override void AfterExecutePlugIn()
        {
            //base.AfterExecutePlugIn();
        }

        /// <summary>
        /// 差件名称
        /// </summary>
        /// <value>用于日志记录的插件名称.</value>
        public override string PlugInName
        {
            get { return @"航段明细方式抢座插件"; }
        }

        #region 拼装SS指令
        /*
        /// <summary>
        /// 拼装指令.
        /// </summary>
        /// <returns></returns>
        protected virtual string createSynCmd(List<PnrPassenger> passenger,)
        {
            //return "SS:CA1537/Y/1OCT/PEKNKGNN3/1225 1400";
            return string.Format("{0}{1}{2}{3}{4}\r{5}"
                , createPassger()
                , createBookAirSeg()
                , createTimelimit()
                , createContact()
                , createSSRFOID()
                , @"@KI"
                );
        }

        /// <summary>
        /// 拼装航段令组.
        /// </summary>
        /// <returns></returns>
        protected virtual string createBookAirSeg()
        {
            StringBuilder sb = new StringBuilder();
            string LastDestination = string.Empty;
            foreach (BookAirSeg airSeg in this.__airSegList)
            {
                sb.AppendFormat("SS:{0} {1} {2} {3}{4} {5}{6}\r"
                    , airSeg.getairNo
                    , airSeg.getfltClass.ToString()
                    , string.Format("{0}{1}{2}", airSeg.departureTime.Day.ToString("D2"), getDateString(airSeg.departureTime), airSeg.departureTime.ToString("yy"))
                    , airSeg.getorgCity
                    , airSeg.getdesCity
                    , string.IsNullOrEmpty(airSeg.getactionCode) ? "NN" : airSeg.getactionCode
                    , this.gettktNum
                    );
                if (!string.IsNullOrEmpty(LastDestination) && !airSeg.getorgCity.Equals(LastDestination))
                    sb.AppendFormat("SA:{0}{1}\r", LastDestination, airSeg.getorgCity);
                LastDestination = airSeg.getdesCity;
            }
            return sb.ToString();
        }

        /// <summary>
        /// Creates the passger.
        /// </summary>
        /// <returns></returns>
        protected virtual string createPassger()
        {
            StringBuilder sb = new StringBuilder("NM");
            foreach (string p in this.__adultList)
            {
                sb.AppendFormat("1{0}", p);
            }
            return sb.Append("\r").ToString();
        }

        /// <summary>
        /// Creates the timelimit.
        /// </summary>
        /// <returns></returns>
        protected virtual string createTimelimit()
        {
            return string.Format("TKTL/{0}/{1}/{2}"
                , this.setTimelimit.Value.ToString("hhmm")
                , string.Format("{0}{1}{2}", this.setTimelimit.Value.Day.ToString("D2"), getDateString(this.setTimelimit.Value), this.setTimelimit.Value.ToString("yy"))
                , this.setOffice
                );
        }

        /// <summary>
        /// Creates the SSRFOID.
        /// </summary>
        /// <returns></returns>
        protected virtual string createSSRFOID()
        {
            StringBuilder sb = new StringBuilder();
            foreach (SSRFOID foid in this.__airSSRFOID)
            {
                sb.AppendFormat("\rSSR FOID {0} HK/{1}{2}/{3}", foid.airline, foid.Passageer.IdType, foid.Passageer.Id, foid.Passageer.Name);
            }
            return sb.ToString();
        }

        /// <summary>
        /// Creates the contact.
        /// </summary>
        /// <returns></returns>
        protected virtual string createContact()
        {
            StringBuilder sb = new StringBuilder();
            foreach (BookContact c in this.__bookContactList)
            {
                sb.AppendFormat("\rCT:{0} {1} {2}", c.getcity, c.psgrName, c.getcontact);
                if (!string.IsNullOrEmpty(c.getcontact))
                    sb.AppendFormat("\rRMK MP {0}", c.getcontact);
            }
            return sb.ToString();
        }
        */
        #endregion

    }
}
